package com.aboverock.core.exception.handler;

import com.aboverock.common.domain.Result;
import com.aboverock.common.enums.MessageSourceKey;
import com.aboverock.core.exception.CustomAuthenticationException;
import com.aboverock.core.exception.CustomLockedAccountException;
import com.aboverock.module.system.i18n.KbSmartMessageSourceKey;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path.Node;
import org.apache.shiro.authz.AuthorizationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import redis.clients.jedis.exceptions.JedisConnectionException;

/**
 * The Class SystemExceptionHandler.
 *
 * @author Rock Wang
 */
@RestControllerAdvice
public class SystemExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(SystemExceptionHandler.class);

    @ExceptionHandler(CustomLockedAccountException.class)
    @ResponseStatus(HttpStatus.LOCKED)
    public Result customLockedAccountExceptionHandler(CustomLockedAccountException e) {
        logger.debug("Locked Account Exception", e);
        return Result.fail("ResultEnum.ACCOUNT_LOCKED");
    }

    @ExceptionHandler(CustomAuthenticationException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Result customAuthenticationExceptionHandler(CustomAuthenticationException e) {
        logger.debug("Authentication Exception", e);
        return Result.fail("ResultEnum.AUTHENCATION_FAIL");
    }

    @ExceptionHandler(AuthorizationException.class)
    public Result authorizationExceptionHandler(AuthorizationException e) {
        logger.debug("Authorization Exception", e);
        return Result.fail("ResultEnum.AUTHORIZATION_FAIL");
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        // 获取参数校验错误集合
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<ObjectError> globalErrors = e.getBindingResult().getGlobalErrors();
        // 格式化以提供友好的错误提示
        Map<String, String> validateResult = new HashMap<>(
                fieldErrors.size() + globalErrors.size());
        fieldErrors.forEach(fieldError -> {
            validateResult.put(fieldError.getField(), fieldError.getDefaultMessage());
        });
        globalErrors.forEach(globalError -> {
            validateResult.put(((Class<?>) globalError.getArguments()[1]).getSimpleName(),
                    globalError.getDefaultMessage());
        });

        return Result.fail(MessageSourceKey.Common.COMMON_PARAM_IS_INVALID_KEY, validateResult);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public Result constraintViolationExceptionHandler(ConstraintViolationException e) {
        // 获取参数校验错误集合
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        // 格式化以提供友好的错误提示
        Map<String, String> validateResult = new HashMap<>(violations.size());
        violations.forEach(violation -> {
            Iterator<Node> iterator = violation.getPropertyPath().iterator();
            while (iterator.hasNext()) {
                Node node = iterator.next();
                switch (node.getKind()) {
                    case PROPERTY:
                        validateResult.put(node.getName(), violation.getMessage());
                        break;
                    case BEAN:
                        validateResult.put(violation.getRootBeanClass().getSimpleName(),
                                violation.getMessage());
                        break;
                    default:
                        break;
                }
            }
        });

        return Result.fail(MessageSourceKey.Common.COMMON_PARAM_IS_INVALID_KEY, validateResult);
    }

    @ExceptionHandler(SQLException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result sqlExceptionHandler(SQLException e) {
        logger.debug("SQL Exception", e);
        return Result.fail(e.getMessage());
    }

    @ExceptionHandler(JedisConnectionException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result jedisConnectionExceptionHandler(JedisConnectionException e) {
        logger.error("Data-Smart：redis connection fail", e);
        return Result.fail(KbSmartMessageSourceKey.REDIS_REQUEST_FAIL, e.getMessage());
    }

}
